{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "9c794bc7",
   "metadata": {},
   "source": [
    "# 构建检索问答链"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f3d0f2c3-3bd9-4de1-bbd2-e0e2b09161c8",
   "metadata": {},
   "source": [
    "在 `C3 搭建数据库` 章节，我们已经介绍了如何根据自己的本地知识文档，搭建一个向量知识库。 在接下来的内容里，我们将使用搭建好的向量数据库，对 query 查询问题进行召回，并将召回结果和 query 结合起来构建 prompt，输入到大模型中进行问答。   "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95d8d968-8d98-47b9-8885-dc17d24dce76",
   "metadata": {},
   "source": [
    "## 1. 加载向量数据库\n",
    "\n",
    "首先，我们加载在前一章已经构建的向量数据库。注意，此处你需要使用和构建时相同的 Emedding。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "8f66d376-8140-4224-bdfb-360b60aef43f",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "sys.path.append(\"../C3 搭建知识库\") # 将父目录放入系统路径中\n",
    "\n",
    "# 使用智谱 Embedding API，注意，需要将上一章实现的封装代码下载到本地\n",
    "from zhipuai_embedding import ZhipuAIEmbeddings\n",
    "\n",
    "from langchain.vectorstores.chroma import Chroma"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "af4a941c-e58d-4515-bdec-55799a4f366a",
   "metadata": {},
   "source": [
    "从环境变量中加载你的 API_KEY"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "59ed5506-9aa2-4777-b594-e97a9148f171",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv, find_dotenv\n",
    "import os\n",
    "\n",
    "_ = load_dotenv(find_dotenv())    # read local .env file\n",
    "zhipuai_api_key = os.environ['ZHIPUAI_API_KEY']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c696e67-1369-4687-86cb-a2e8101a2f38",
   "metadata": {},
   "source": [
    "加载向量数据库，其中包含了 ../../data_base/knowledge_db 下多个文档的 Embedding"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "1e277ec2-28e4-448d-ae39-e0f703017811",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义 Embeddings\n",
    "embedding = ZhipuAIEmbeddings()\n",
    "\n",
    "# 向量数据库持久化路径\n",
    "persist_directory = '../../data_base/vector_db/chroma'\n",
    "\n",
    "# 加载数据库\n",
    "vectordb = Chroma(\n",
    "    persist_directory=persist_directory,  # 允许我们将persist_directory目录保存到磁盘上\n",
    "    embedding_function=embedding\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "7b0b1838-38a3-4666-8bc8-c4592a5a39d8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "向量库中存储的数量：925\n"
     ]
    }
   ],
   "source": [
    "print(f\"向量库中存储的数量：{vectordb._collection.count()}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d64f262-0638-4aa3-bd89-ef74e33f5238",
   "metadata": {},
   "source": [
    "我们可以测试一下加载的向量数据库，使用一个问题 query 进行向量检索。如下代码会在向量数据库中根据相似性进行检索，返回前 k 个最相似的文档。\n",
    "\n",
    "> ⚠️使用相似性搜索前，请确保你已安装了 OpenAI 开源的快速分词工具 tiktoken 包：`pip install tiktoken`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c8a68dc0-4f4c-433b-a367-44b5cffe8516",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "检索到的内容数：3\n"
     ]
    }
   ],
   "source": [
    "question = \"什么是prompt engineering?\"\n",
    "docs = vectordb.similarity_search(question,k=3)\n",
    "print(f\"检索到的内容数：{len(docs)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "09852d2a-aeda-4822-bf56-782a0397df3c",
   "metadata": {},
   "source": [
    "打印一下检索到的内容"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d2f492bf-197b-4f94-820c-2d88c17754d6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "检索到的第0个内容: \n",
      " 相反，我们应通过 Prompt 指引语言模型进行深入思考。可以要求其先列出对问题的各种看法，说明推理依据，然后再得出最终结论。在 Prompt 中添加逐步推理的要求，能让语言模型投入更多时间逻辑思维，输出结果也将更可靠准确。\n",
      "\n",
      "综上所述，给予语言模型充足的推理时间，是 Prompt Engineering 中一个非常重要的设计原则。这将大大提高语言模型处理复杂问题的效果，也是构建高质量 Prompt 的关键之处。开发者应注意给模型留出思考空间，以发挥语言模型的最大潜力。\n",
      "\n",
      "2.1 指定完成任务所需的步骤\n",
      "\n",
      "接下来我们将通过给定一个复杂任务，给出完成该任务的一系列步骤，来展示这一策略的效果。\n",
      "\n",
      "首先我们描述了杰克和吉尔的故事，并给出提示词执行以下操作：首先，用一句话概括三个反引号限定的文本。第二，将摘要翻译成英语。第三，在英语摘要中列出每个名称。第四，输出包含以下键的 JSON 对象：英语摘要和人名个数。要求输出以换行符分隔。\n",
      "-----------------------------------------------------\n",
      "检索到的第1个内容: \n",
      " 该描述面向家具零售商，因此应具有技术性质，并侧重于产品的材料构造。\n",
      "\n",
      "在描述末尾，包括技术规格中每个7个字符的产品ID。\n",
      "\n",
      "使用最多50个单词。\n",
      "\n",
      "技术规格： {fact_sheet_chair}\n",
      "\"\"\"\n",
      "response = get_completion(prompt)\n",
      "print(response)\n",
      "```\n",
      "\n",
      "通过上面的示例，我们可以看到 Prompt 迭代优化的一般过程。与训练机器学习模型类似，设计高效 Prompt 也需要多个版本的试错调整。\n",
      "\n",
      "具体来说，第一版 Prompt 应该满足明确和给模型思考时间两个原则。在此基础上，一般的迭代流程是：首先尝试一个初版，分析结果，然后继续改进 Prompt，逐步逼近最优。许多成功的Prompt 都是通过这种多轮调整得出的。\n",
      "\n",
      "后面我会展示一个更复杂的 Prompt 案例，让大家更深入地了解语言模型的强大能力。但在此之前，我想强调 Prompt 设计是一个循序渐进的过程。开发者需要做好多次尝试和错误的心理准备，通过不断调整和优化，才能找到最符合具体场景需求的 Prompt  形式。这需要智慧和毅力，但结果往往是值得的。\n",
      "-----------------------------------------------------\n",
      "检索到的第2个内容: \n",
      " 第二章 提示原则\n",
      "\n",
      "如何去使用 Prompt，以充分发挥 LLM 的性能？首先我们需要知道设计 Prompt 的原则，它们是每一个开发者设计 Prompt 所必须知道的基础概念。本章讨论了设计高效 Prompt 的两个关键原则：编写清晰、具体的指令和给予模型充足思考时间。掌握这两点，对创建可靠的语言模型交互尤为重要。\n",
      "\n",
      "首先，Prompt 需要清晰明确地表达需求，提供充足上下文，使语言模型准确理解我们的意图，就像向一个外星人详细解释人类世界一样。过于简略的 Prompt 往往使模型难以把握所要完成的具体任务。\n",
      "\n",
      "其次，让语言模型有充足时间推理也极为关键。就像人类解题一样，匆忙得出的结论多有失误。因此 Prompt 应加入逐步推理的要求，给模型留出充分思考时间，这样生成的结果才更准确可靠。\n",
      "\n",
      "如果 Prompt 在这两点上都作了优化，语言模型就能够尽可能发挥潜力，完成复杂的推理和生成任务。掌握这些 Prompt 设计原则，是开发者取得语言模型应用成功的重要一步。\n",
      "\n",
      "一、原则一 编写清晰、具体的指令\n",
      "-----------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "for i, doc in enumerate(docs):\n",
    "    print(f\"检索到的第{i}个内容: \\n {doc.page_content}\", end=\"\\n-----------------------------------------------------\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4f7f8dbd-ecd5-449d-9753-aedc2b74289c",
   "metadata": {},
   "source": [
    "## 2. 创建一个 LLM"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "026bd74f-3dd0-496e-905b-950a444bb7a7",
   "metadata": {},
   "source": [
    "在这里，我们调用 OpenAI 的 API 创建一个 LLM，当然你也可以使用其他 LLM 的 API 进行创建"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "745d50cd",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os \n",
    "OPENAI_API_KEY = os.environ[\"OPENAI_API_KEY\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "fca9fa08-ce50-478f-b0a4-c24166262dc5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='你好，我是一个智能助手，专门为您提供各种服务和帮助。我可以回答您的问题，提供信息和建议，帮助您解决问题。如果您有任何需要，请随时告诉我，我会尽力帮助您。感谢您选择我作为您的助手！', response_metadata={'token_usage': {'completion_tokens': 95, 'prompt_tokens': 20, 'total_tokens': 115}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-bf5e56bd-a00f-4370-a1f3-6ce9712eaa65-0')"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "llm = ChatOpenAI(model_name = \"gpt-3.5-turbo\", temperature = 0)\n",
    "\n",
    "llm.invoke(\"请你自我介绍一下自己！\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f361e27-cafb-48bf-bb41-50c9cb3a4f7e",
   "metadata": {},
   "source": [
    "## 3. 构建检索问答链"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "91be03f4-264d-45cb-bebd-223c1c5747fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.prompts import PromptTemplate\n",
    "\n",
    "template = \"\"\"使用以下上下文来回答最后的问题。如果你不知道答案，就说你不知道，不要试图编造答\n",
    "案。最多使用三句话。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问！”。\n",
    "{context}\n",
    "问题: {question}\n",
    "\"\"\"\n",
    "\n",
    "QA_CHAIN_PROMPT = PromptTemplate(input_variables=[\"context\",\"question\"],\n",
    "                                 template=template)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2d06d7f-1dca-4d10-b5cd-3a23e9d91200",
   "metadata": {},
   "source": [
    "再创建一个基于模板的检索链："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "8b05eb57-edf5-4b35-9538-42c2b8f5cc16",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chains import RetrievalQA\n",
    "\n",
    "qa_chain = RetrievalQA.from_chain_type(llm,\n",
    "                                       retriever=vectordb.as_retriever(),\n",
    "                                       return_source_documents=True,\n",
    "                                       chain_type_kwargs={\"prompt\":QA_CHAIN_PROMPT})\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c08ac734-6693-422d-97e1-50f140e2d358",
   "metadata": {},
   "source": [
    "创建检索 QA 链的方法 RetrievalQA.from_chain_type() 有如下参数：\n",
    "- llm：指定使用的 LLM\n",
    "- 指定 chain type : RetrievalQA.from_chain_type(chain_type=\"map_reduce\")，也可以利用load_qa_chain()方法指定chain type。\n",
    "- 自定义 prompt ：通过在RetrievalQA.from_chain_type()方法中，指定chain_type_kwargs参数，而该参数：chain_type_kwargs = {\"prompt\": PROMPT}\n",
    "- 返回源文档：通过RetrievalQA.from_chain_type()方法中指定：return_source_documents=True参数；也可以使用RetrievalQAWithSourceChain()方法，返回源文档的引用（坐标或者叫主键、索引）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "503a7972-a673-41ca-a028-647169d19fcb",
   "metadata": {},
   "source": [
    "## 4.检索问答链效果测试"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "a610e223-64c2-4865-b049-b47a36262a50",
   "metadata": {},
   "outputs": [],
   "source": [
    "question_1 = \"什么是南瓜书？\"\n",
    "question_2 = \"Prompt Engineering for Developer是谁写的？\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "acc2223f-6fb5-4504-bfcd-ac74ca9ff2fa",
   "metadata": {},
   "source": [
    "### 4.1 基于召回结果和 query 结合起来构建的 prompt 效果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "1eb5c5c3-9958-44f5-9fbc-f867de6c5042",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/lta/anaconda3/envs/llm_universe_2.x/lib/python3.10/site-packages/langchain_core/_api/deprecation.py:119: LangChainDeprecationWarning: The method `Chain.__call__` was deprecated in langchain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "大模型+知识库后回答 question_1 的结果：\n",
      "南瓜书是对《机器学习》（西瓜书）中难以理解的公式进行解析和补充推导的书籍。\n",
      "谢谢你的提问！\n"
     ]
    }
   ],
   "source": [
    "result = qa_chain({\"query\": question_1})\n",
    "print(\"大模型+知识库后回答 question_1 的结果：\")\n",
    "print(result[\"result\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "37a0f3c4-4c50-4b73-a4b3-674825a366f1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "大模型+知识库后回答 question_2 的结果：\n",
      "Prompt Engineering for Developer是由吴恩达老师与OpenAI技术团队成员Isa Fulford老师合作编写的。谢谢你的提问！\n"
     ]
    }
   ],
   "source": [
    "result = qa_chain({\"query\": question_2})\n",
    "print(\"大模型+知识库后回答 question_2 的结果：\")\n",
    "print(result[\"result\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4195cfa-1fc8-41a9-8984-91f2e5fbe013",
   "metadata": {},
   "source": [
    "### 4.2 大模型自己回答的效果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "569fbe28-2e2d-4042-b3a1-65326842bdc9",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/lta/anaconda3/envs/llm_universe_2.x/lib/python3.10/site-packages/langchain_core/_api/deprecation.py:119: LangChainDeprecationWarning: The method `BaseChatModel.predict` was deprecated in langchain-core 0.1.7 and will be removed in 0.3.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'南瓜书是指一本内容浅显易懂，适合初学者阅读的书籍。这个词源自于日本，最初是指一种用南瓜做成的灯笼，形状简单易制作。后来，这个词被引申为指那些内容简单易懂，适合初学者入门的书籍。'"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prompt_template = \"\"\"请回答下列问题:\n",
    "                            {}\"\"\".format(question_1)\n",
    "\n",
    "### 基于大模型的问答\n",
    "llm.predict(prompt_template)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "d0d3a813-db19-4be5-8926-ad8298e3e2b1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'这本书是由作者 Chris Johnson 编写的。'"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prompt_template = \"\"\"请回答下列问题:\n",
    "                            {}\"\"\".format(question_2)\n",
    "\n",
    "### 基于大模型的问答\n",
    "llm.predict(prompt_template)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "51b9ba4a-053d-409a-a632-63336c2bdf84",
   "metadata": {},
   "source": [
    "> ⭐ 通过以上两个问题，我们发现 LLM 对于一些近几年的知识以及非常识性的专业问题，回答的并不是很好。而加上我们的本地知识，就可以帮助 LLM 做出更好的回答。另外，也有助于缓解大模型的“幻觉”问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad20c510-f583-42b4-ac5c-b232388e9673",
   "metadata": {},
   "source": [
    "## 5. 添加历史对话的记忆功能"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "321e43e5-262d-4358-afb4-129dbf413287",
   "metadata": {},
   "source": [
    "现在我们已经实现了通过上传本地知识文档，然后将他们保存到向量知识库，通过将查询问题与向量知识库的召回结果进行结合输入到 LLM 中，我们就得到了一个相比于直接让 LLM 回答要好得多的结果。在与语言模型交互时，你可能已经注意到一个关键问题 - **它们并不记得你之前的交流内容**。这在我们构建一些应用程序（如聊天机器人）的时候，带来了很大的挑战，使得对话似乎缺乏真正的连续性。这个问题该如何解决呢？\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8cadc3df-4123-4e12-9516-8632b10dc41f",
   "metadata": {},
   "source": [
    "## 1. 记忆（Memory）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e19f3fd2-14de-4177-893c-53ddaf6768e9",
   "metadata": {},
   "source": [
    "在本节中我们将介绍 LangChain 中的储存模块，即如何将先前的对话嵌入到语言模型中的，使其具有连续对话的能力。我们将使用 `ConversationBufferMemory` ，它保存聊天消息历史记录的列表，这些历史记录将在回答问题时与问题一起传递给聊天机器人，从而将它们添加到上下文中。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "42210b88-3590-47dc-a087-ab9cef14f00c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.memory import ConversationBufferMemory\n",
    "\n",
    "memory = ConversationBufferMemory(\n",
    "    memory_key=\"chat_history\",  # 与 prompt 的输入变量保持一致。\n",
    "    return_messages=True  # 将以消息列表的形式返回聊天记录，而不是单个字符串\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "48d580d9-b926-462f-9d6a-23135ecece37",
   "metadata": {},
   "source": [
    "关于更多的 Memory 的使用，包括保留指定对话轮数、保存指定 token 数量、保存历史对话的总结摘要等内容，请参考 langchain 的 Memory 部分的相关文档。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2ec5f5c-5ee8-4b89-add3-25f3c864200d",
   "metadata": {},
   "source": [
    "## 2. 对话检索链（ConversationalRetrievalChain）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "65f1afd4-a5e7-4eea-855b-d03055c93e2d",
   "metadata": {},
   "source": [
    "对话检索链（ConversationalRetrievalChain）在检索 QA 链的基础上，增加了处理对话历史的能力。\n",
    "\n",
    "它的工作流程是:\n",
    "1. 将之前的对话与新问题合并生成一个完整的查询语句。\n",
    "2. 在向量数据库中搜索该查询的相关文档。\n",
    "3. 获取结果后,存储所有答案到对话记忆区。\n",
    "4. 用户可在 UI 中查看完整的对话流程。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad1705dc-db31-4663-beb0-01f14e2ca5da",
   "metadata": {},
   "source": [
    "![](../../figures/Modular_components.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a0305623-1a06-4bb8-b340-dde57202dd67",
   "metadata": {},
   "source": [
    "这种链式方式将新问题放在之前对话的语境中进行检索，可以处理依赖历史信息的查询。并保留所有信\n",
    "息在对话记忆中，方便追踪。\n",
    "\n",
    "接下来让我们可以测试这个对话检索链的效果："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "05dd113e-9d21-4385-a426-7e7a8ebb887a",
   "metadata": {},
   "source": [
    "使用上一节中的向量数据库和 LLM ！首先提出一个无历史对话的问题“这门课会学习 Python 吗？”，并查看回答。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "2d5dcc5f-545e-463a-9f3f-9eca90457caa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "是的，您可以学习关于提示工程的知识。本模块基于吴恩达老师的《Prompt Engineering for Developer》课程编写，旨在与开发者分享使用提示词开发大语言模型应用的最佳实践和技巧。通过学习这些内容，您将了解如何使用提示词来构建令人惊叹的大语言模型应用。希望这对您有所帮助！\n"
     ]
    }
   ],
   "source": [
    "from langchain.chains import ConversationalRetrievalChain\n",
    "\n",
    "retriever=vectordb.as_retriever()\n",
    "\n",
    "qa = ConversationalRetrievalChain.from_llm(\n",
    "    llm,\n",
    "    retriever=retriever,\n",
    "    memory=memory\n",
    ")\n",
    "question = \"我可以学习到关于提示工程的知识吗？\"\n",
    "result = qa({\"question\": question})\n",
    "print(result['answer'])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ad80344-7079-4fa2-a3f6-065f1a0a2d79",
   "metadata": {},
   "source": [
    "然后基于答案进行下一个问题“为什么这门课需要教这方面的知识？”："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "83ef314c-515d-499c-8358-f19f188895b4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "这门课程的目的是教授开发者如何使用提示工程来开发大型语言模型（LLM）应用程序。通过学习如何编写清晰具体的指令以及给模型思考时间等核心原则，开发者可以更好地利用大型语言模型来解决各种任务，提高工作效率和创造力。\n"
     ]
    }
   ],
   "source": [
    "question = \"为什么这门课需要教这方面的知识？\"\n",
    "result = qa({\"question\": question})\n",
    "print(result['answer'])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10b44d8b-145d-42fc-b790-476798efdabe",
   "metadata": {},
   "source": [
    "可以看到，LLM 它准确地判断了这方面的知识，指代内容是强化学习的知识，也就\n",
    "是我们成功地传递给了它历史信息。这种持续学习和关联前后问题的能力，可大大增强问答系统的连续\n",
    "性和智能水平。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "llm2",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
